﻿Imports System.Xml
Imports System.ComponentModel
Imports Tools.Textt
'Tools for import from so-called simple file
Namespace ImportExport.Simple
    ''' <summary><see cref="ĐŘječnik.Dictionary"/> that can load itself from XML file of Simple.xsd type</summary>
    Public Class Dictionary : Inherits ĐŘječnik.Dictionary
        ''' <summary>CTor from XML document</summary>
        ''' <param name="doc">XML to import data from</param>
        ''' <exception cref="ImportException">Error during import</exception>
        ''' <exception cref="OperationCanceledException"><see cref="BackgroundWorker.CancellationPending"/> of <paramref name="ReportProgress"/> is set to True</exception>
        ''' <remarks>XML document must be of &lt;Dictionary> type as described in Simple.xsd</remarks>
        ''' <param name="ReportProgress"><see cref="BackgroundWorker"/> to report progress through. Can be also used to cancel the operation</param>
        Public Sub New(ByVal doc As XmlDocument, Optional ByVal ReportProgress As BackgroundWorker = Nothing)
            Try
                If ReportProgress IsNot Nothing AndAlso ReportProgress.WorkerReportsProgress Then ReportProgress.ReportProgress(0, My.Resources.LoadingDictionaryInformations)
                'Load information
                Dim Dictionary As XmlElement = doc.DocumentElement
                If Dictionary.Name <> tagDictionary Then Throw New ImportException(String.Format(My.Resources.errRootMustBe, "<" & tagDictionary & ">"))
                Dim Info As XmlElement = Dictionary.GetElementsByTagName(tagInfo)(0)
                If Info.Name <> tagInfo Then Throw New ImportException(String.Format(My.Resources.errFirstChildMustBe, "<" & tagDictionary & ">", "<" & tagInfo & ">"))
                ImportInfo(Info)
                Dim Primarys As XmlNodeList = Info.GetElementsByTagName(tagPrimary)
                If Primarys.Count > 0 Then Primary = New PrimaryInfo(Primarys(0))
                Author = New AuthorInfo(Info.GetElementsByTagName(tagAuthor)(0))
                Dim Originals As XmlNodeList = Info.GetElementsByTagName(tagOriginal)
                If Originals.Count > 0 Then Original = New OriginalAuthorInfo(Originals(0))
                'Load dictionary data
                If ReportProgress IsNot Nothing AndAlso ReportProgress.WorkerReportsProgress Then ReportProgress.ReportProgress(0, My.Resources.LoadingTranslationPairs)
                Dim xmlPairs As XmlNodeList = Dictionary.GetElementsByTagName(tagPair)
                Dim i As Integer = 0
                For Each Pair As XmlElement In xmlPairs : i += 1
                    If ReportProgress IsNot Nothing Then
                        If ReportProgress.WorkerReportsProgress Then
                            ReportProgress.ReportProgress(i / xmlPairs.Count * 100)
                        End If
                        If ReportProgress.CancellationPending Then Throw New OperationCanceledException(My.Resources.LoadingOfDictionaryCanceled)
                    End If
                    Pairs.Add(New TranslationPair(Pair, Pairs))
                Next Pair
            Catch ex As Exception
                If TypeOf ex Is OperationCanceledException Then Throw
                Throw New ImportException(My.Resources.ErrorDuringImport & " " & ex.Message, ex)
            End Try
        End Sub

        ''' <summary>COntains value of the <see cref="Pairs"/> property</summary>
        <EditorBrowsable(EditorBrowsableState.Never)> Private _Pairs As New List(Of TranslationPair)
        ''' <summary>Loaded translation pairs</summary>
        Public Property Pairs() As List(Of TranslationPair)
            Get
                Return _Pairs
            End Get
            Set(ByVal value As List(Of TranslationPair))
                _Pairs = value
            End Set
        End Property
        ''' <summary>Represents translation pair of words</summary>
        <DebuggerDisplay("{ToString}")> _
        Public Class TranslationPair
            ''' <summary>Returns a <see cref="System.String"/> that represents the current <see cref="TranslationPair"/>.</summary>
            ''' <returns>A <see cref="System.String"/> that represents the current <see cref="TranslationPair"/></returns>
            Public Overrides Function ToString() As String
                If Me.Words1.Count > 0 AndAlso Me.Words2.Count > 0 Then
                    Return Me.Words1(0).Native & " - " & Me.Words2(0).Native
                ElseIf Me.Words1.Count > 0 Then
                    Return Me.Words1(0).Native & " - "
                ElseIf Me.Words2.Count > 0 Then
                    Return " - " & Me.Words2(0).Native
                Else
                    Return " - "
                End If
            End Function

            ''' <summary>CTor from XML element</summary>
            ''' <param name="Pair">The &lt;pair> (or &lt;relatedword>) XML element to load pair from</param>
            ''' <param name="AdditionalPairsSaveTo">If pair contains nested pairs, save them here</param>
            Public Sub New(ByVal Pair As XmlElement, ByVal AdditionalPairsSaveTo As IList(Of TranslationPair))
                Dim AtLeast1 As Boolean = False
                For Each W1 As XmlElement In Pair.GetElementsByTagName(tagW1) : AtLeast1 = True
                    Words1.Add(New Word(W1, AdditionalPairsSaveTo))
                Next W1
                If Not AtLeast1 Then Throw New ImportException(String.Format(My.Resources.ThereMustBeAtLeast1InTranslationPair, "<" & tagW1 & ">", "<" & tagPair & ">"))
                AtLeast1 = False
                For Each W2 As XmlElement In Pair.GetElementsByTagName(tagW2) : AtLeast1 = True
                    Words2.Add(New Word(W2, AdditionalPairsSaveTo))
                Next W2
                If Not AtLeast1 Then Throw New ImportException(String.Format(My.Resources.ThereMustBeAtLeast1InTranslationPair, "<" & tagW2 & ">", "<" & tagPair & ">"))
                For Each Rel As XmlElement In Pair.GetElementsByTagName(tagRel)
                    If Rel.ParentNode Is Pair Then
                        Dim NestedPair As New TranslationPair(Rel.GetElementsByTagName(tagRelatedWord)(0), AdditionalPairsSaveTo)
                        AdditionalPairsSaveTo.Add(NestedPair)
                        Dim Relations1 As New List(Of Relation)
                        For Each W1 As Word In NestedPair.Words1
                            Relations1.Add(New Relation(Rel.GetAttribute(attrRel), W1.Native))
                        Next W1
                        Dim Relations2 As New List(Of Relation)
                        For Each W2 As Word In NestedPair.Words2
                            Relations2.Add(New Relation(Rel.GetAttribute(attrRel), W2.Native))
                        Next W2
                        For Each W1 As Word In Words1
                            For Each Rel1 As Relation In Relations1
                                W1.Relations.Add(Rel1)
                            Next Rel1
                        Next W1
                        For Each W2 As Word In Words2
                            For Each Rel2 As Relation In Relations2
                                W2.Relations.Add(Rel2)
                            Next Rel2
                        Next W2
                    End If
                Next Rel
            End Sub

            ''' <summary>Contains value of the <see cref="Words1"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _Words1 As New List(Of Word)
            ''' <summary>List of words in first language</summary>
            Public Property Words1() As List(Of Word)
                Get
                    Return _Words1
                End Get
                Set(ByVal value As List(Of Word))
                    _Words1 = value
                End Set
            End Property
            ''' <summary>Contains value of the <see cref="Words2"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _Words2 As New List(Of Word)
            ''' <summary>List of words in second language</summary>
            Public Property Words2() As List(Of Word)
                Get
                    Return _Words2
                End Get
                Set(ByVal value As List(Of Word))
                    _Words2 = value
                End Set
            End Property
        End Class

        ''' <summary>Represents single word with its relations</summary>
        <DebuggerDisplay("{Native} {Latin} ({Type})")> _
        Public Class Word
            ''' <summary>Returns a <see cref="System.String"/> that represents the current <see cref="Word"/>.</summary>
            ''' <returns>A <see cref="System.String"/> that represents the current <see cref="Word"/></returns>
            Public Overrides Function ToString() As String
                Return Me.Native
            End Function
            ''' <summary>Contains value of the <see cref="Latin"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _Latin As String
            ''' <summary>Optional transliteration of word if <see cref="Native"/> is not in Latin</summary>
            Public Property Latin() As String
                Get
                    Return _Latin
                End Get
                Set(ByVal value As String)
                    _Latin = value
                End Set
            End Property
            ''' <summary>Contains value of the <see cref="Type"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _Type As enmType
            ''' <summary>Type of word</summary>
            Public Property Type() As enmType
                Get
                    Return _Type
                End Get
                Set(ByVal value As enmType)
                    _Type = value
                End Set
            End Property
            ''' <summary>Contains value of the <see cref="Native"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _Native As String
            ''' <summary>Text of word in native alphabet of its language</summary>
            Public Property Native() As String
                Get
                    Return _Native
                End Get
                Set(ByVal value As String)
                    _Native = value
                End Set
            End Property
            ''' <summary>Contains value of the <see cref="Relations"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _Relations As New List(Of Relation)
            ''' <summary>List of related words</summary>
            Public Property Relations() As List(Of Relation)
                Get
                    Return _Relations
                End Get
                Set(ByVal value As List(Of Relation))
                    _Relations = value
                End Set
            End Property
            ''' <summary>Contains value of the <see cref="Note"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _Note As String
            ''' <summary>Note to word</summary>
            Public Property Note() As String
                Get
                    Return _Note
                End Get
                Set(ByVal value As String)
                    _Note = value
                End Set
            End Property

            ''' <summary>Conzains value of the <see cref="ID"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _ID As Long
            ''' <summary>ID of word in database</summary>
            Public Property ID() As Long
                Get
                    Return _ID
                End Get
                Set(ByVal value As Long)
                    _ID = value
                End Set
            End Property
            ''' <summary>CTor from XML element</summary>
            ''' <param name="W">XML element &lt;W1> or &lt;W2> that contains information about word</param>
            ''' <param name="AdditionalPairsSaveTo"></param> 
            Public Sub New(ByVal W As XmlElement, ByVal AdditionalPairsSaveTo As List(Of TranslationPair))
                Native = MTrim(W.GetAttribute(attrN))
                If Native = "" Then Throw New ImportExport.ImportException(String.Format(My.Resources.AttributeMissingOrEmpty, attrN))
                Latin = MTrim(W.GetAttribute(attrL))
                Select Case W.GetAttribute(valType).Trim
                    Case valNoun
                        Type = enmType.Noun
                    Case valAdj
                        Type = enmType.Adjective
                    Case valPron
                        Type = enmType.Pronoun
                    Case valNum
                        Type = enmType.Numeral
                    Case valVerb
                        Type = enmType.Verb
                    Case valAdv
                        Type = enmType.Adverb
                    Case valPrep
                        Type = enmType.Preposition
                    Case valConj
                        Type = enmType.Conjuction
                    Case valPart
                        Type = enmType.Particle
                    Case valIntj
                        Type = enmType.Interjection
                    Case valPhrase
                        Type = enmType.Phrase
                    Case valArt
                        Type = enmType.Article
                    Case Else
                        Type = enmType.NA
                End Select
                Dim Notes As XmlNodeList = W.GetElementsByTagName(tagNote)
                If Notes.Count > 0 Then Me.Note = MTrim(Notes(0).InnerText, True)
                For Each Rel As XmlElement In W.GetElementsByTagName(tagRel)
                    Dim Node As XmlElement
                    Try
                        Node = Rel.GetElementsByTagName("relw").Item(0)
                    Catch
                        Continue For
                    End Try
                    If Node IsNot Nothing Then
                        Relations.Add(New Relation(Rel.GetAttribute(attrRel), MTrim(Node.GetAttribute(attrWord))))
                    Else
                        Node = Rel.FirstChild(tagRelatedWord)
                        If Node IsNot Nothing Then
                            Dim RelatedPair As New TranslationPair(Node, AdditionalPairsSaveTo)
                            AdditionalPairsSaveTo.Add(RelatedPair)
                            If W.Name = tagW1 Then
                                For Each W1 As Word In RelatedPair.Words1
                                    Relations.Add(New Relation(Rel.GetAttribute(attrRel), W1.Native))
                                Next W1
                            ElseIf W.Name = tagW2 Then
                                For Each W2 As Word In RelatedPair.Words2
                                    Relations.Add(New Relation(Rel.GetAttribute(attrRel), W2.Native))
                                Next W2
                            End If
                        End If
                    End If
                Next Rel
            End Sub
        End Class

        ''' <summary>Represents relation between words</summary>
        <DebuggerDisplay("{Rel} {Word}")> _
        Public Class Relation
            ''' <summary>CTor from XML value and word</summary>
            ''' <param name="Relation">XML keyword for relation</param>
            ''' <param name="Word">Word to establish relation to</param>
            Public Sub New(ByVal Relation As String, ByVal Word As String)
                Select Case Relation.Trim
                    Case valPhrase
                        Rel = enmRelation.Phrase
                    Case valSeealso
                        Rel = enmRelation.SeeAlso
                    Case Else
                        Throw New ImportException(String.Format(My.Resources.UnknownRelation, Relation))
                End Select
                Me.Word = Word
            End Sub
            ''' <summary>CTor</summary>
            ''' <param name="Relation">Relation to establish between words</param>
            ''' <param name="Word">Wort to establish relation to</param>
            Public Sub New(ByVal Relation As enmRelation, ByVal Word As String)
                Me.Rel = Relation
                Me.Word = Word
            End Sub
            ''' <summary>Contains value of the <see cref="Rel"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _Rel As enmRelation
            ''' <summary>Type of relation</summary>
            Public Property Rel() As enmRelation
                Get
                    Return _Rel
                End Get
                Set(ByVal value As enmRelation)
                    _Rel = value
                End Set
            End Property
            ''' <summary>Contains value of the <see cref="Word"/> property</summary>
            <EditorBrowsable(EditorBrowsableState.Never)> Private _Word As String
            ''' <summary>Text of related word</summary>
            Public Property Word() As String
                Get
                    Return _Word
                End Get
                Set(ByVal value As String)
                    _Word = value
                End Set
            End Property
        End Class
        ''' <summary>Contains value of te <see cref="Path"/> property</summary>
        Private _Path As Tools.IOt.Path
        ''' <summary>Path of dictionary file</summary>
        ''' <remarks>Used to resolve icons</remarks>
        Public Property Path() As Tools.IOt.Path
            Get
                Return _Path
            End Get
            Set(ByVal value As Tools.IOt.Path)
                _Path = value
            End Set
        End Property
    End Class
End Namespace